﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Drawing;
using System.Threading;
using System.IO;
using System.IO.Ports;

namespace ScanDevConsole
{
    public class ScanDeviceProcess
    {
        public Action<string, System.Drawing.Brush> toastInfoDel;
        public bool reqQuitRun = false;
        public string portname = "COM1";    //串口号
        public int baudrate = 19200;        //波特率
        public int slaveID = 1;             //Modbus设备 从机ID

        private const int pduTimeOut = 50;      //Modbus 一个PDU超时的时间 ms

        private int modbusErrCnt = 0;

        private const UInt16 Addr_Inputs1 = 30;     //输入口信息 保持寄存器的地址
        private const UInt16 Addr_DacCfg = 10;      //输出波形设置集保持寄存器起始地址 
        private const UInt16 Addr_LissajousParams = 40; //利萨如图形参数 起始地址
        public bool input_ProbeType = false;        // 探头类型信号
        public bool input_Capture = false;          // 抓图信号
        public bool input_VideoCap = false;         // 录像信号

        public struct ScanDevParamsSZ
        {
            public int probeType;      //输出探头类型 0:MEMS 1:振镜
            public float x_start_v;    //x轴输出起始和结束电压
            public float x_end_v;      //
            public int x_stepPerRange; //x轴扫描步数
            public uint x_freq_div;    //x轴分频
            public float y_start_v;    //y轴输出起始和结束电压
            public float y_end_v;      //
            public int y_stepPerRange; //y轴扫描步数
            public uint y_freq_div;    //y轴分频
            public int wave_phase_shift;   //输出波形相移

            public float y_AmpScale;       //y轴输出电压幅值缩放
            public int scanShape;          //输出形状
        }

        public ScanDevParamsSZ scanParams;  //扫描参数
        public bool cmdApplyScanParams = false; //执行新的参数到设备

        public struct LissajousParam   //利萨如图形 参数
        {
            public float Amp_x;     //x轴的电压幅值
            public float Amp_y;     //y轴的电压幅值
            public float Freq_x;     //x轴的频率
            public float Freq_y;     //y轴的频率
            public float Phase_x;     //x轴的频率
            public float Phase_y;     //y轴的频率
        }

        public LissajousParam lissajousParam;
        public bool cmdApplyLissajousParams = false; //执行新的利萨如图形参数到设备


        private const UInt16 Addr_RedLight = 16;    //辅助红光 线圈地址
        public bool redLightOn = false;     //是否打开辅助红光
        public bool cmdRedLight = false;    //刷新辅助红光状态命令

        private const UInt16 Addr_ResetRectPos = 17;    //镜镜模式时，输出波形复位
        public bool cmdResetRectPos = false;             //复位镜镜模式时的输出波形

        private const UInt16 Addr_InteralTrig = 62;     //使用内部触发 线圈地址
        public bool cmdSetInteralTrig = false;      //设置设备使用内部触发

        private const UInt16 Addr_MemsWaveData = 0x0E000;   //MEMS波形数据 保持寄存起始地址
        public bool cmdUpdateWaveData = false;      //更新MEMS波形数据命令
        public bool cmdReadWave = false;            //读MEMS波形数据命令
        public string saveCSVFileName = null;       //读MEMS波形数据命令保存到的文件名
        public UInt16[] memsWaveData;               //MEMS波形数据

        public ScanDeviceProcess()
        {
            //byte[] d = new byte[] { 0x01, 0x03, 0xFF, 0xFF, 0x10, 0x00 };
            //UInt16 res = ModbusHelp.crc16(ref d);
            //Debug.WriteLine(BitConverter.ToString(BitConverter.GetBytes(res)));

            //UInt16[] regs = new UInt16[] { 60000, 20000, 0, 0, 0, 0, 0, 0, 0, 0 };
            //byte[] wpdu;
            //ModbusHelp.generateWriteRegsPDU(out wpdu, 1, 30000, ref regs);
            //Debug.WriteLine(BitConverter.ToString(wpdu));

            //ModbusHelp.calcAndAppendCRC(ref wpdu);
            //Debug.WriteLine(BitConverter.ToString(wpdu));

            //byte[] rpdu;
            //ModbusHelp.generateReadRegsPDU(out rpdu, 01, 0xf000, 4);
            //Debug.WriteLine(BitConverter.ToString(rpdu));

            //byte[] recvpdu = new byte[] { 0x01, 0x03, 0x08, 0x10, 0x00, 0x10, 0x02, 0x30, 0x03, 0x40, 0x44, 0x21, 0x78 };
            //UInt16[] readedregs;
            //ModbusHelp.decodeReadRegsResponse(ref rpdu, ref recvpdu, out readedregs);
            //if (readedregs != null)
            //{
            //    foreach(UInt16 r in readedregs)
            //    {
            //        Debug.Write(string.Format("{0:X} ", r));
            //    }
            //    Debug.Write("\n");
            //}

            //byte[] wcoilpdu;
            //byte[] coils = new byte[] { 1, 0, 0, 1, 0, 1, 1, 0 };
            //ModbusHelp.generateWriteCoilsPDU(out wcoilpdu, 1, 0xf000, ref coils);
            //Debug.WriteLine(BitConverter.ToString(wcoilpdu));

            scanParams = new ScanDevParamsSZ();
            scanParams.probeType = 1;
            scanParams.scanShape = 0;
            scanParams.x_stepPerRange = 500;
            scanParams.x_freq_div = 1;
            scanParams.x_start_v = 2.5f;
            scanParams.x_end_v = -2.5f;
            scanParams.y_stepPerRange = 500;
            scanParams.y_freq_div = 1;
            scanParams.y_start_v = 2.5f;
            scanParams.y_end_v = -2.5f;
            scanParams.y_AmpScale = 1.0f;
            scanParams.wave_phase_shift = 0;

            lissajousParam.Amp_x = 5.0f;
            lissajousParam.Amp_y = 5.0f;
            lissajousParam.Freq_x = 50.0f;
            lissajousParam.Freq_y = 50.0f;
            lissajousParam.Phase_x = 0;
            lissajousParam.Phase_y = 0;


        }

        //返回是否通信失败
        public bool IsCommFailed
        {
            get
            {
                return modbusErrCnt >= 2;
            }
        }

        private bool readRegsFromDev(ref SerialPort serialport, UInt16 addr, UInt16 len, out UInt16[] regs) //从设备 读回保持寄存器的值
        {
            regs = null;
            if (len < 1 || slaveID == 0) return true;

            for (int i = 0; i < 3; i++) //尝试执行三次通信
            {
                serialport.DiscardInBuffer();
                serialport.DiscardOutBuffer();

                //生成读 寄存器 pdu  并发送pdu
                byte[] sendpdu;
                ModbusHelp.generateReadRegsPDU(out sendpdu, slaveID, addr, len);
                //Debug.WriteLine(BitConverter.ToString(sendpdu));
                try
                {
                    serialport.Write(sendpdu, 0, sendpdu.Length);
                }
                catch
                {
                    if (modbusErrCnt < 10)
                    {
                        toastInfo("Modbus 发送超时", Brushes.Red);
                    }
                    if (i == 2 && modbusErrCnt < 10)
                    {
                        modbusErrCnt++;
                    }
                    if (i == 2) return false;
                    continue;
                }

                //接收回复
                byte[] recvpdu = new byte[512];
                int readedCnt = 0;

                serialport.ReadTimeout = pduTimeOut * 2;   //第一个字节时间长一点
                try
                {
                    readedCnt += serialport.Read(recvpdu, readedCnt, 1);
                }
                catch
                {

                }
                serialport.ReadTimeout = pduTimeOut;
                while (true)    //继续读取剩余的字节
                {
                    int rb = 0;
                    try
                    {
                        rb = serialport.Read(recvpdu, readedCnt, recvpdu.Length - readedCnt);
                    }
                    catch
                    {
                    }

                    if (rb < 1) break;
                    else readedCnt += rb;

                    if (readedCnt >= 512) break;
                }

                if (readedCnt < 1)
                {
                    if (modbusErrCnt < 10)
                    {
                        toastInfo("Modbus 接收超时", Brushes.Red);
                    }
                    if (i == 2 && modbusErrCnt < 10)
                    {
                        modbusErrCnt++;
                    }
                    if (i == 2) return false;
                    continue;
                }

                //解码回复
                byte[] tt = recvpdu.Take(readedCnt).ToArray();
                //Debug.WriteLine(BitConverter.ToString(tt));

                bool isreadOk = ModbusHelp.decodeReadRegsResponse(ref sendpdu, ref tt, out regs);

                if (isreadOk && regs != null && regs.Length > 0)
                {
                    if (modbusErrCnt >= 10)
                    {
                        toastInfo("Modbus 连接恢复", Brushes.Green);
                    }
                    modbusErrCnt = 0;
                    break;
                }
                else
                {
                    if (modbusErrCnt < 10)
                    {
                        toastInfo("Modbus 接收到错误的回复", Brushes.Red);
                    }
                    if (i == 2 && modbusErrCnt < 10)
                    {
                        modbusErrCnt++;
                    }
                    if (i == 2) return false;
                }

            }   //尝试执行三次通信

            return true;
        }

        private void readInputFromModbus(ref SerialPort serialport)
        {
            for (int i = 0; i < 3; i++) //尝试执行三次通信
            {
                serialport.DiscardInBuffer();
                serialport.DiscardOutBuffer();

                //生成读 寄存器 pdu  并发送pdu
                byte[] sendpdu;
                ModbusHelp.generateReadRegsPDU(out sendpdu, slaveID, Addr_Inputs1, 1);
                //Debug.WriteLine(BitConverter.ToString(sendpdu));
                try
                {
                    serialport.Write(sendpdu, 0, sendpdu.Length);
                }
                catch
                {
                    if (modbusErrCnt < 10)
                    {
                        toastInfo("Modbus 发送超时", Brushes.Red);
                    }
                    if (i == 2 && modbusErrCnt < 10)
                    {
                        modbusErrCnt++;
                    }
                    continue;
                }

                if (slaveID == 0) break;    //广播时 不进行回复检查

                //接收回复
                byte[] recvpdu = new byte[512];
                int readedCnt = 0;

                serialport.ReadTimeout = pduTimeOut*2;   //第一个字节时间长一点
                try
                {
                    readedCnt += serialport.Read(recvpdu, readedCnt, 1);
                }
                catch
                {

                }
                serialport.ReadTimeout = pduTimeOut;
                while (true)    //继续读取剩余的字节
                {
                    int rb = 0;
                    try
                    {
                        rb = serialport.Read(recvpdu, readedCnt, recvpdu.Length - readedCnt);
                    }
                    catch
                    {
                    }
                    
                    if (rb < 1) break;
                    else readedCnt += rb;

                    if (readedCnt >= 512) break;
                }

                if (readedCnt < 1)
                {
                    if (modbusErrCnt < 10)
                    {
                        toastInfo("Modbus 接收超时", Brushes.Red);
                    }
                    if (i == 2  && modbusErrCnt < 10)
                    {
                        modbusErrCnt++;
                    }
                    continue;
                }

                //解码回复
                byte[] tt = recvpdu.Take(readedCnt).ToArray();
                //Debug.WriteLine(BitConverter.ToString(tt));

                UInt16[] inputs;
                bool isreadOk = ModbusHelp.decodeReadRegsResponse(ref sendpdu, ref tt, out inputs);

                if (isreadOk && inputs != null && inputs.Length > 0)
                {
                    input_ProbeType = (inputs[0] & 0x01) != 0;  //获取输入口结果
                    input_Capture = (inputs[0] & 0x02) != 0;
                    input_VideoCap = (inputs[0] & 0x04) != 0;
                    if (modbusErrCnt >= 10)
                    {
                        toastInfo("Modbus 连接恢复", Brushes.Green);
                    }
                    modbusErrCnt = 0;
                    break;
                }
                else
                {
                    if (modbusErrCnt < 10)
                    {
                        toastInfo("Modbus 接收到错误的回复", Brushes.Red);
                    }
                    if (i == 2 && modbusErrCnt < 10)
                    {
                        modbusErrCnt++;
                    }
                }

            }   //尝试执行三次通信

            
        }

        private bool writeRegsToDev(ref SerialPort serialport,UInt16 addr,ref UInt16[] regs)    //写数据到设备的保持寄存器 成功返回true
        {
            if (regs == null) return true;
            if (regs.Length < 1) return true;

            for (int i = 0; i < 3; i++) //尝试执行三次通信
            {
                serialport.DiscardInBuffer();
                serialport.DiscardOutBuffer();

                //生成读 寄存器 pdu  并发送pdu
                byte[] sendpdu;
                ModbusHelp.generateWriteRegsPDU(out sendpdu, slaveID, addr, ref regs);
                ModbusHelp.calcAndAppendCRC(ref sendpdu);
                //Debug.WriteLine(BitConverter.ToString(sendpdu));
                try
                {
                    serialport.Write(sendpdu, 0, sendpdu.Length);
                }
                catch
                {
                    if (modbusErrCnt < 10)
                    {
                        toastInfo("Modbus 发送超时", Brushes.Red);
                    }
                    if (i == 2 && modbusErrCnt < 10)
                    {
                        modbusErrCnt++;
                    }
                    if (i == 2) return false;
                    continue;
                }

                if (slaveID == 0) break;    //广播时 不进行回复检查

                //接收回复
                byte[] recvpdu = new byte[512];
                int readedCnt = 0;

                serialport.ReadTimeout = pduTimeOut * 2;   //第一个字节时间长一点
                try
                {
                    readedCnt += serialport.Read(recvpdu, readedCnt, 1);
                }
                catch
                {

                }
                serialport.ReadTimeout = pduTimeOut;
                while (true)    //继续读取剩余的字节
                {
                    int rb = 0;
                    try
                    {
                        rb = serialport.Read(recvpdu, readedCnt, recvpdu.Length - readedCnt);
                    }
                    catch
                    {
                    }

                    if (rb < 1) break;
                    else readedCnt += rb;

                    if (readedCnt >= 512) break;
                }

                if (readedCnt < 1)
                {
                    if (modbusErrCnt < 10)
                    {
                        toastInfo("Modbus 接收超时", Brushes.Red);
                    }
                    if (i == 2 && modbusErrCnt < 10)
                    {
                        modbusErrCnt++;
                    }
                    if (i == 2) return false;
                    continue;
                }

                //解码回复
                byte[] tt = recvpdu.Take(readedCnt).ToArray();
                //Debug.WriteLine(BitConverter.ToString(tt));
                bool isreadOk = ModbusHelp.isPDU_CRC_OK(ref tt);    //检查CRC是否正确
                for (int j = 0; j < 6; j++)
                {
                    isreadOk = isreadOk && (tt[j] == sendpdu[j]);
                    if (!isreadOk) break;
                }

                if (!isreadOk)
                {
                    if (modbusErrCnt < 10)
                    {
                        toastInfo("Modbus 接收到错误的回复", Brushes.Red);
                    }
                    if (i == 2 && modbusErrCnt < 10)
                    {
                        modbusErrCnt++;
                    }
                    if (i == 2) return false;
                }
                else
                {
                    if (modbusErrCnt >= 10)
                    {
                        toastInfo("Modbus 连接恢复", Brushes.Green);
                    }
                    modbusErrCnt = 0;
                    break;
                }

            }   //尝试执行三次通信
            return true;
        }

        private void applyScanParamsToDev(ref SerialPort serialport)
        {
            for (int i = 0; i < 3; i++) //尝试执行三次通信
            {
                serialport.DiscardInBuffer();
                serialport.DiscardOutBuffer();

                //将要设置的参数转换成 保持寄存器
                UInt16[] regs = new UInt16[11];
                regs[0] = (UInt16)scanParams.probeType;
                regs[1] = (UInt16)((Int32)(scanParams.x_end_v * 1000) & 0x0FFFF);
                regs[2] = (UInt16)((Int32)(scanParams.x_start_v * 1000) & 0x0FFFF);
                regs[3] = (UInt16)scanParams.x_freq_div;
                regs[4] = (UInt16)scanParams.x_stepPerRange;
                regs[5] = (UInt16)((Int32)(scanParams.y_end_v * 1000) & 0x0FFFF);
                regs[6] = (UInt16)((Int32)(scanParams.y_start_v * 1000) & 0x0FFFF);
                //regs[7] = (UInt16)scanParams.y_freq_div;
                regs[7] = (UInt16)scanParams.wave_phase_shift;
                regs[8] = (UInt16)scanParams.y_stepPerRange;
                regs[9] = (UInt16)((Int32)(scanParams.y_AmpScale * 1000) & 0x0FFFF);
                regs[10] = (UInt16)scanParams.scanShape;

                //生成读 寄存器 pdu  并发送pdu
                byte[] sendpdu;
                ModbusHelp.generateWriteRegsPDU(out sendpdu, slaveID, Addr_DacCfg, ref regs);
                ModbusHelp.calcAndAppendCRC(ref sendpdu);
                //Debug.WriteLine(BitConverter.ToString(sendpdu));
                try
                {
                    serialport.Write(sendpdu, 0, sendpdu.Length);
                }
                catch
                {
                    if (modbusErrCnt < 10)
                    {
                        toastInfo("Modbus 发送超时", Brushes.Red);
                    }
                    if (i == 2 && modbusErrCnt < 10)
                    {
                        modbusErrCnt++;
                    }
                    continue;
                }

                if (slaveID == 0) break;    //广播时 不进行回复检查

                //接收回复
                byte[] recvpdu = new byte[512];
                int readedCnt = 0;

                serialport.ReadTimeout = pduTimeOut * 2;   //第一个字节时间长一点
                try
                {
                    readedCnt += serialport.Read(recvpdu, readedCnt, 1);
                }
                catch
                {

                }
                serialport.ReadTimeout = pduTimeOut;
                while (true)    //继续读取剩余的字节
                {
                    int rb = 0;
                    try
                    {
                        rb = serialport.Read(recvpdu, readedCnt, recvpdu.Length - readedCnt);
                    }
                    catch
                    {
                    }

                    if (rb < 1) break;
                    else readedCnt += rb;

                    if (readedCnt >= 512) break;
                }

                if (readedCnt < 1)
                {
                    if (modbusErrCnt < 10)
                    {
                        toastInfo("Modbus 接收超时", Brushes.Red);
                    }
                    if (i == 2 && modbusErrCnt < 10)
                    {
                        modbusErrCnt++;
                    }
                    continue;
                }

                //解码回复
                byte[] tt = recvpdu.Take(readedCnt).ToArray();
                //Debug.WriteLine(BitConverter.ToString(tt));
                bool isreadOk = ModbusHelp.isPDU_CRC_OK(ref tt);    //检查CRC是否正确
                for (int j = 0; j < 6; j++)
                {
                    isreadOk = isreadOk && (tt[j] == sendpdu[j]);
                    if (!isreadOk) break;
                }

                if (!isreadOk)
                {
                    if (modbusErrCnt < 10)
                    {
                        toastInfo("Modbus 接收到错误的回复", Brushes.Red);
                    }
                    if (i == 2 && modbusErrCnt < 10)
                    {
                        modbusErrCnt++;
                    }
                }
                else
                {
                    if (modbusErrCnt >= 10)
                    {
                        toastInfo("Modbus 连接恢复", Brushes.Green);
                    }
                    modbusErrCnt = 0;
                    break;
                }

            }   //尝试执行三次通信
        }

        private void applyLissajousParamsToDev(ref SerialPort serialport)
        {
            for (int i = 0; i < 3; i++) //尝试执行三次通信
            {
                serialport.DiscardInBuffer();
                serialport.DiscardOutBuffer();

                //将要设置的参数转换成 保持寄存器
                UInt16[] regs = new UInt16[6];
                regs[0] = (UInt16)((Int32)(lissajousParam.Amp_x * 1000) & 0x0FFFF);
                regs[1] = (UInt16)((Int32)(lissajousParam.Amp_y * 1000) & 0x0FFFF);
                regs[2] = (UInt16)((Int32)(lissajousParam.Freq_x * 10) & 0x0FFFF);
                regs[3] = (UInt16)((Int32)(lissajousParam.Freq_y * 10) & 0x0FFFF);
                regs[4] = (UInt16)((Int32)(lissajousParam.Phase_x * 100) & 0x0FFFF);
                regs[5] = (UInt16)((Int32)(lissajousParam.Phase_y * 100) & 0x0FFFF);

                //生成读 寄存器 pdu  并发送pdu
                byte[] sendpdu;
                ModbusHelp.generateWriteRegsPDU(out sendpdu, slaveID, Addr_LissajousParams, ref regs);
                ModbusHelp.calcAndAppendCRC(ref sendpdu);
                //Debug.WriteLine(BitConverter.ToString(sendpdu));
                try
                {
                    serialport.Write(sendpdu, 0, sendpdu.Length);
                }
                catch
                {
                    if (modbusErrCnt < 10)
                    {
                        toastInfo("Modbus 发送超时", Brushes.Red);
                    }
                    if (i == 2 && modbusErrCnt < 10)
                    {
                        modbusErrCnt++;
                    }
                    continue;
                }

                if (slaveID == 0) break;    //广播时 不进行回复检查

                //接收回复
                byte[] recvpdu = new byte[512];
                int readedCnt = 0;

                serialport.ReadTimeout = pduTimeOut * 2;   //第一个字节时间长一点
                try
                {
                    readedCnt += serialport.Read(recvpdu, readedCnt, 1);
                }
                catch
                {

                }
                serialport.ReadTimeout = pduTimeOut;
                while (true)    //继续读取剩余的字节
                {
                    int rb = 0;
                    try
                    {
                        rb = serialport.Read(recvpdu, readedCnt, recvpdu.Length - readedCnt);
                    }
                    catch
                    {
                    }

                    if (rb < 1) break;
                    else readedCnt += rb;

                    if (readedCnt >= 512) break;
                }

                if (readedCnt < 1)
                {
                    if (modbusErrCnt < 10)
                    {
                        toastInfo("Modbus 接收超时", Brushes.Red);
                    }
                    if (i == 2 && modbusErrCnt < 10)
                    {
                        modbusErrCnt++;
                    }
                    continue;
                }

                //解码回复
                byte[] tt = recvpdu.Take(readedCnt).ToArray();
                //Debug.WriteLine(BitConverter.ToString(tt));
                bool isreadOk = ModbusHelp.isPDU_CRC_OK(ref tt);    //检查CRC是否正确
                for (int j = 0; j < 6; j++)
                {
                    isreadOk = isreadOk && (tt[j] == sendpdu[j]);
                    if (!isreadOk) break;
                }

                if (!isreadOk)
                {
                    if (modbusErrCnt < 10)
                    {
                        toastInfo("Modbus 接收到错误的回复", Brushes.Red);
                    }
                    if (i == 2 && modbusErrCnt < 10)
                    {
                        modbusErrCnt++;
                    }
                }
                else
                {
                    if (modbusErrCnt >= 10)
                    {
                        toastInfo("Modbus 连接恢复", Brushes.Green);
                    }
                    modbusErrCnt = 0;
                    break;
                }

            }   //尝试执行三次通信
        }

        private void setCoils(ref SerialPort serialport, UInt16 addr, ref byte[] coilsValue)
        {
            for (int i = 0; i < 3; i++) //尝试执行三次通信
            {
                serialport.DiscardInBuffer();
                serialport.DiscardOutBuffer();

                //生成读 寄存器 pdu  并发送pdu
                byte[] sendpdu;
                ModbusHelp.generateWriteCoilsPDU(out sendpdu, slaveID, addr, ref coilsValue);
                //Debug.WriteLine(BitConverter.ToString(sendpdu));
                try
                {
                    serialport.Write(sendpdu, 0, sendpdu.Length);
                }
                catch
                {
                    if (modbusErrCnt < 10)
                    {
                        toastInfo("Modbus 发送超时", Brushes.Red);
                    }
                    if (i == 2 && modbusErrCnt < 10)
                    {
                        modbusErrCnt++;
                    }
                    continue;
                }

                if (slaveID == 0) break;    //广播时 不进行回复检查

                //接收回复
                byte[] recvpdu = new byte[512];
                int readedCnt = 0;

                serialport.ReadTimeout = pduTimeOut * 2;   //第一个字节时间长一点
                try
                {
                    readedCnt += serialport.Read(recvpdu, readedCnt, 1);
                }
                catch
                {

                }
                serialport.ReadTimeout = pduTimeOut;
                while (true)    //继续读取剩余的字节
                {
                    int rb = 0;
                    try
                    {
                        rb = serialport.Read(recvpdu, readedCnt, recvpdu.Length - readedCnt);
                    }
                    catch
                    {
                    }

                    if (rb < 1) break;
                    else readedCnt += rb;

                    if (readedCnt >= 512) break;
                }

                if (readedCnt < 1)
                {
                    if (modbusErrCnt < 10)
                    {
                        toastInfo("Modbus 接收超时", Brushes.Red);
                    }
                    if (i == 2 && modbusErrCnt < 10)
                    {
                        modbusErrCnt++;
                    }
                    continue;
                }

                //解码回复
                byte[] tt = recvpdu.Take(readedCnt).ToArray();
                //Debug.WriteLine(BitConverter.ToString(tt));
                bool isreadOk = ModbusHelp.isPDU_CRC_OK(ref tt);    //检查CRC是否正确
                for (int j = 0; j < 6; j++)
                {
                    isreadOk = isreadOk && (tt[j] == sendpdu[j]);
                    if (!isreadOk) break;
                }

                if (!isreadOk)
                {
                    if (modbusErrCnt < 10)
                    {
                        toastInfo("Modbus 接收到错误的回复", Brushes.Red);
                    }
                    if (i == 2 && modbusErrCnt < 10)
                    {
                        modbusErrCnt++;
                    }
                }
                else
                {
                    if (modbusErrCnt >= 10)
                    {
                        toastInfo("Modbus 连接恢复", Brushes.Green);
                    }
                    modbusErrCnt = 0;
                    break;
                }

            }   //尝试执行三次通信
        }

        private void doWriteToDev(ref SerialPort serialport)      //执行写入通信到设备
        {
            if (cmdApplyScanParams)
            {
                modbusErrCnt = 0;
                applyScanParamsToDev(ref serialport);
                cmdApplyScanParams = false;
            }

            if (cmdApplyLissajousParams)
            {
                modbusErrCnt = 0;
                applyLissajousParamsToDev(ref serialport);
                cmdApplyLissajousParams = false;
            }

            if (cmdRedLight)
            {
                modbusErrCnt = 0;
                byte[] coilsValue = new byte[] { 0 };
                coilsValue[0] = Convert.ToByte(redLightOn);
                setCoils(ref serialport, Addr_RedLight, ref coilsValue);
                cmdRedLight = false;
            }

            if (cmdResetRectPos) {
                modbusErrCnt = 0;
                byte[] coilsValue = new byte[] { 1 };
                setCoils(ref serialport, Addr_ResetRectPos, ref coilsValue);
                cmdResetRectPos = false;
            }

            if (cmdSetInteralTrig)
            {
                byte[] coilsValue = new byte[] { 1 };
                setCoils(ref serialport, Addr_InteralTrig, ref coilsValue);
                cmdSetInteralTrig = false;
            }

            if (cmdUpdateWaveData)
            {
                UInt16 addr = Addr_MemsWaveData;
                bool writeRegsOK = true;
                bool readCheckOK = true;
                int sendRegsCnt = 0;
                int eachbatch = 32;
                int loop = memsWaveData.Length / 32;
                if (memsWaveData.Length % 32 > 0) loop++;
                if (memsWaveData.Length < 32) eachbatch = memsWaveData.Length;

                toastInfo(null, null);  //清除信息
                for (int i = 0; i < loop; ++i)
                {
                    UInt16[] temp = memsWaveData.Skip(sendRegsCnt).Take(eachbatch).ToArray();
                    writeRegsOK = writeRegsOK && writeRegsToDev(ref serialport, addr, ref temp);    //写入数
                    if (!writeRegsOK) break;

                    if (readCheckOK)                //回读数据 检查写入的是否正确
                    {
                        if (i == loop - 1) Thread.Sleep(3000);  //等待三秒 MCU擦除flash
                        UInt16[] readback=null;
                        readCheckOK = readCheckOK && readRegsFromDev(ref serialport, addr, (UInt16)eachbatch, out readback);
                        readCheckOK = readCheckOK && (readback != null);
                        if(readCheckOK) readCheckOK = readCheckOK && (readback.Length >= temp.Length);
                        for (int j=0; readCheckOK && j < readback.Length && j < eachbatch; ++j)
                        {
                            readCheckOK = readCheckOK && readback[j] == temp[j];
                        }
                    }

                    if (reqQuitRun) break;

                    sendRegsCnt += eachbatch;
                    addr += (UInt16)eachbatch;
                    if (i == (loop - 2) && (memsWaveData.Length - sendRegsCnt) < 32)
                    {
                        eachbatch = memsWaveData.Length - sendRegsCnt;
                    }

                    // 显示写入百分比
                    float f = sendRegsCnt;
                    f = f / memsWaveData.Length;
                    toastInfo(string.Format("写入波形数据进度 {0:D3}%", (int)(f * 100.0f)), Brushes.Black);
                }

                if (writeRegsOK && !reqQuitRun)
                {
                    toastInfo("更新MEMS波形数据完毕!", Brushes.Green);
                }
                else
                {
                    toastInfo("更新MEMS波形数据失败!", Brushes.Red);
                }

                if (!readCheckOK)
                {
                    toastInfo("写入的波形数据与回读的不一致 或者回读数据出错!", Brushes.Red);
                }

                cmdUpdateWaveData = false;
            }
        }

        private void doReadFromDev(ref SerialPort serialport)    //执行通信从设备读取
        {
            readInputFromModbus(ref serialport);

            if (cmdReadWave)            //读取波形数据并保存
            {
                const UInt16 totalRegCnt = 8 * 1024;
                UInt16 addr = Addr_MemsWaveData;
                bool readOK = true;
                int readRegsCnt = 0;
                int eachbatch = 32;
                int loop = totalRegCnt / 32;
                if (totalRegCnt % 32 > 0) loop++;
                if (totalRegCnt < 32) eachbatch = totalRegCnt;
                UInt16[] readback = new UInt16[totalRegCnt];

                toastInfo(null, null);  //清除信息

                //创建并打开保存CSV文件
                FileStream fs = null;
                StreamWriter sw = null;
                try
                {
                    fs = new FileStream(saveCSVFileName, System.IO.FileMode.Create, System.IO.FileAccess.Write);
                    sw = new StreamWriter(fs, System.Text.Encoding.ASCII);
                }
                catch
                {
                }
                
                if(fs == null || sw == null)
                {
                    readOK = false;
                    toastInfo(string.Format("创建或覆盖文件 '{0}' 失败!", saveCSVFileName), Brushes.Red);
                }

                for (int i = 0; readOK && i < loop; ++i)
                {
                    if (readOK)                //读数据 放置到缓冲区
                    {
                        UInt16[] temp=null;
                        readOK = readOK && readRegsFromDev(ref serialport, addr, (UInt16)eachbatch, out temp);
                        readOK = readOK && (temp != null);
                        if (readOK) readOK = readOK && (temp.Length >= eachbatch);
                        for (int j = 0; readOK && j < eachbatch; ++j)
                        {
                            if (!readOK) break;
                            readback[readRegsCnt + j] = temp[j];
                        }
                    }else
                    {
                        break;
                    }

                    if (reqQuitRun)
                    {
                        readOK = false;
                        break;
                    }

                    readRegsCnt += eachbatch;
                    addr += (UInt16)eachbatch;
                    if (i == (loop - 2) && (totalRegCnt - readRegsCnt) < 32)
                    {
                        eachbatch = totalRegCnt - readRegsCnt;
                    }

                    // 显示写入百分比
                    float f = readRegsCnt;
                    f = f / totalRegCnt;
                    toastInfo(string.Format("读取波形数据进度 {0:D3}%", (int)(f * 100.0f)), Brushes.Black);
                }

                if (readOK)
                {
                    toastInfo("读取MEMS波形数据完毕，执行写入CSV文件.", Brushes.Green);
                    //将数据写入CSV文件
                    int halfCnt = readback.Length / 2;
                    for(int j = 0; readOK && j < halfCnt; ++j)
                    {
                        string str = string.Format("{0}, {1}, \n", readback[j], readback[j + halfCnt]);
                        try
                        {
                            sw.Write(str);
                        }
                        catch {
                            readOK = false;
                        }
                    }

                    if(readOK) toastInfo("波形数据写入CSV文件成功.", Brushes.Green);
                    else toastInfo("波形数据写入CSV文件失败!", Brushes.Red);
                }
                else
                {
                    toastInfo("读取MEMS波形数据失败!", Brushes.Red);
                }

                if(sw != null)
                {
                    sw.Close();
                    sw = null;
                }

                if (fs != null)
                {
                    fs.Close();
                    fs = null;
                }

                readback = null;

                cmdReadWave = false;
            }
        }

        public void ScanDevRun()        //线程运行的函数入口
        {
            //打开串口
            SerialPort serialport = new SerialPort();
            serialport.PortName = portname;
            serialport.BaudRate = baudrate;
            serialport.DataBits = 8;
            serialport.StopBits = System.IO.Ports.StopBits.One;
            serialport.Parity = System.IO.Ports.Parity.Even;
            serialport.RtsEnable = false;
            serialport.DtrEnable = false;
            serialport.WriteTimeout = 500;

            modbusErrCnt = 0;

            toastInfo(string.Format("准备打开串口 {0}, 波特率 {1} , 设备ID {2}",
                serialport.PortName, serialport.BaudRate, slaveID
                ),
                Brushes.Green);

            try
            {
                serialport.Open();
            }
            catch
            {

            }

            if (!serialport.IsOpen)
            {
                toastInfo(string.Format("打开串口 {0} 失败!", serialport.PortName), Brushes.Red);
                return;
            }
            else
            {
                toastInfo(string.Format("打开串口 {0} 成功!", serialport.PortName), Brushes.Green);
            }

            /////////////////////////////////////////////////////////////////////

            while (true)        //线程循环
            {
                //执行写入通信到设备
                doWriteToDev(ref serialport);

                //执行通信从设备读取
                doReadFromDev(ref serialport);

                if (reqQuitRun) break;
                if (modbusErrCnt >= 10)
                {
                    System.Threading.Thread.Sleep(1000);
                }
                else
                {
                    System.Threading.Thread.Sleep(10);
                }
                    
            }

            ///////////////////////////////////////////////////////////////////

            serialport.Close();
            toastInfo(string.Format("关闭串口 {0} ", serialport.PortName), Brushes.Green);

            toastInfo("设备读写线程结束", Brushes.Black);
            reqQuitRun = false;
        }

        private void toastInfo(string info, System.Drawing.Brush color)
        {
            if (toastInfoDel != null)
            {
                toastInfoDel(info, color);
            }
        }
    }
}
